#include "HerzeleidPrecompiled.hxx"
#include "HerzeleidAudioStreamer.hxx"
using namespace HerzeleidMediaNamespaceImpl;

#ifndef MAKEFOURCC
#define MakeFourCc( ch0, ch1, ch2, ch3 ) \
	((UInt32)(Byte)(ch0) | \
	((UInt32)(Byte)(ch1) << 8) | \
	((UInt32)(Byte)(ch2) << 16) | \
	((UInt32)(Byte)(ch3) << 24 )) 
#else
#define MakeFourCc MAKEFOURCC
#endif

#define RiffFourCc	MakeFourCc('R','I','F','F') // Riff chunk
#define WaveFourCc	MakeFourCc('W','A','V','E') // Audio type chunk
#define FtmFourCc	MakeFourCc('f','t','m',' ') // Format chunk
#define DataFourCc	MakeFourCc('d','a','t','a') // Data chunk

AudioStreamer::AudioStreamer() : m_uOffset(0u)
{
	Standard::ZeroValue( &m_OutputWaveFormat );
}

Void AudioStreamer::Initialize( _In_ LPCWSTR wszUrl )
{
#ifdef HERZELEID_DESKTOP
	ReaderPtr pReader( new AudioStreamer::Reader() );
	AudioStreamer::Reader::ByteArrayPtr pBuffer = pReader->ReadData( 
		Reader::StringArg(new String16(wszUrl)) 
		);
#else
	ReaderPtr pReader = ref new AudioStreamer::Reader();
	AudioStreamer::Reader::ByteArrayPtr pBuffer = pReader->ReadData( 
		ref new Platform::String(wszUrl) 
		);
#endif
	const auto pData = pBuffer->Data; // Buffer context
	auto uLength = pBuffer->Length; // Buffer length
	auto uWaveFormatSize = sizeof(Media::WaveFormat);
	auto uDwordSize = (UInt32)sizeof(UInt32);
	auto uRiffDataLength = 0u;
	auto uOffset = 0u;

	auto ReadChunk = [&]( UInt32 uFourCc, UInt32Ref uOutChunkSize, UInt32Ref uOutChunkPos )
	{
		while (true)
		{
			if ( (uOffset + uDwordSize * 2) >= uLength )
			{
				return( E_FAIL );
			} // end if

			auto uChunkType = (*reinterpret_cast<UInt32ConstPtr>( &pData[uOffset] ));
			auto uChunkSize = (*reinterpret_cast<UInt32ConstPtr>( &pData[uOffset] + uDwordSize ));
			uOffset += uDwordSize * 2;

			if ( uChunkType == RiffFourCc )
			{
				uRiffDataLength = uChunkSize;
				uChunkSize = uDwordSize;
				uOutChunkSize = uDwordSize;
			} // end if
			else
			{
				uOutChunkSize = uChunkSize;
			} // end else

			uOutChunkPos = uOffset;
			uOffset += uChunkSize;

			if ( uChunkType == uFourCc )
			{
				return S_OK;
			} // end if
		} // end while 
	}; // end func ptr def

	auto uChunkSize = 0u;
	auto uChunkPos = 0u;

	// 1> Locate RIFF chunk
	DirectX::ThrowIfFailed( ReadChunk(RiffFourCc, uChunkSize, uChunkPos) );

	// 2> Check file type
	auto FileType = (*reinterpret_cast<UInt32ConstPtr>( &pData[uOffset] ));
	DirectX::ThrowIfFailed( (FileType == WaveFourCc) ? S_OK : E_NOTIMPL );

	// 3> Locate ftm chunk and copy to wave format struct
	DirectX::ThrowIfFailed( ReadChunk(FtmFourCc, uChunkSize, uChunkPos) );
	DirectX::ThrowIfFailed( (uChunkSize == uWaveFormatSize) ? S_OK : E_FAIL );
	CopyMemory( &m_OutputWaveFormat, &pData[uChunkPos], uChunkSize );

	// 4> Locate the data chunk and copy its content to buffer 
	DirectX::ThrowIfFailed( ReadChunk(DataFourCc, uChunkSize, uChunkPos) ); 

#ifdef HERZELEID_DESKTOP
	m_Data.Length = uChunkSize; 
	// Resize the data buffer so we can copy all the chunk to.
	CopyMemory( m_Data.Data, &pData[uChunkPos], uChunkSize );
#else // Metro Or Phone
	m_Data = ref new Platform::Array<Byte>(uChunkSize);
	// Resize the data buffer so we can copy all the chunk to.
	CopyMemory( m_Data->Data, &pData[uChunkPos], uChunkSize );
#endif // !(Metro Or Phone)

	// 5> Ensure the offset is zero. 
	m_uOffset = 0;
}

Void AudioStreamer::ReadAllMemory( 
	_Outptr_ BytePtr pBuffer, _In_ UInt32 uMaxBufferSize, 
	_Outptr_ UInt32Ptr pBufferSize 
	)
{
#ifdef HERZELEID_DESKTOP
	auto uBytesToCopy = m_Data.Length;
	if ( uBytesToCopy > uMaxBufferSize ) 
		uBytesToCopy = uMaxBufferSize;

	CopyMemory( pBuffer, m_Data.Data, uBytesToCopy );
#else // Metro Or Phone
	auto uBytesToCopy = m_Data->Length;
	if ( uBytesToCopy > uMaxBufferSize ) 
		uBytesToCopy = uMaxBufferSize;

	CopyMemory( pBuffer, m_Data->Data, uBytesToCopy );
#endif // !(Metro Or Phone)
	(*pBufferSize) = uBytesToCopy;
}

Void AudioStreamer::ReadNextMemoryChunk( 
	_Outptr_ BytePtr pBuffer, _In_ UInt32 uDesiredBufferSize, 
	_Outptr_ UInt32Ptr pBufferSize 
	)
{
#ifdef HERZELEID_DESKTOP
	auto uBytesToCopy = m_Data.Length - m_uOffset;
	if ( uBytesToCopy > uDesiredBufferSize ) 
		uBytesToCopy = uDesiredBufferSize;

	CopyMemory( pBuffer, m_Data.Data + m_uOffset, uBytesToCopy );
	(*pBufferSize) = uBytesToCopy;
	m_uOffset += uBytesToCopy;

	if ( m_uOffset > m_Data.Length )
		m_uOffset = m_Data.Length;
#else // Metro Or Phone
	auto uBytesToCopy = m_Data->Length - m_uOffset;
	if ( uBytesToCopy > uDesiredBufferSize ) 
		uBytesToCopy = uDesiredBufferSize;

	CopyMemory( pBuffer, m_Data->Data + m_uOffset, uBytesToCopy );
	(*pBufferSize) = uBytesToCopy;
	m_uOffset += uBytesToCopy;

	if ( m_uOffset > m_Data->Length )
		m_uOffset = m_Data->Length;
#endif // !(Metro Or Phone)
}

Void AudioStreamer::Restart()
{
	m_uOffset = 0u;
}
